#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include "chipmuck-simconn.h"
#include "../errno.h"
#include "../simconndata.h"

int fifo_fd[2];
int chipmuck_available;
struct simconn_callback qemu_callback = {0};

void simconn_connect() {
	if ((fifo_fd[0] = open("chipmuck-fifo-out", O_RDONLY | O_NONBLOCK)) < 0)
		goto err1;
	if ((fifo_fd[1] = open("chipmuck-fifo-in", O_WRONLY)) < 0)
		goto err2;
	chipmuck_available = 1;
	return;
err2:
	close(fifo_fd[0]);
err1:
	chipmuck_available = 0;
}

void simconn_breakpoint(int index) {
	char buf[256];
	int packid, cont = 0;
	struct pack_bp_met pack = {index};

	if (chipmuck_available) {
		write_pack(fifo_fd[1], PACKID_BP_MET, &pack, sizeof(pack));

		while (!cont) {
			while (read_pack(fifo_fd[0], &packid, buf, 256, 10) < 0);
			switch (packid) {
			case PACKID_BP_SET:
				{
					struct pack_bp_set_ret ret;
					struct pack_bp_set *param = (struct pack_bp_set *)buf;

					ret.ret = -E_NOT_IMPLEMENTED;
					if (qemu_callback.set_breakpoint) {
						char data[256];

						read_data(fifo_fd[0], data, param->datasize);
						ret.ret = qemu_callback.set_breakpoint(param->type, data, param->datasize, &ret.id);
					}
					write_pack(fifo_fd[1], PACKID_BP_SET_RET, &ret, sizeof(ret));
				}
				break;
			case PACKID_BP_REMOVE:
				{
					struct pack_bp_remove_ret ret;
					struct pack_bp_remove *param = (struct pack_bp_remove *)buf;

					ret.ret = -E_NOT_IMPLEMENTED;
					if (qemu_callback.del_breakpoint)
						ret.ret = qemu_callback.del_breakpoint(param->id);
					write_pack(fifo_fd[1], PACKID_BP_REMOVE_RET, &ret, sizeof(ret));
				}
				break;
			case PACKID_MEM_DUMP:
				{
					char data[4096];
					struct pack_mem_dump_ret ret;
					struct pack_mem_dump *param = (struct pack_mem_dump *)buf;

					ret.ret = -E_NOT_IMPLEMENTED;
					if (qemu_callback.dump_data) {
						if (param->size <= 4096)
							ret.ret = qemu_callback.dump_data(param->addr, data, param->size);
						else
							ret.ret = -E_INVALID_PARAM;
					}
					write_pack(fifo_fd[1], PACKID_MEM_DUMP_RET, &ret, sizeof(ret));
					if (ret.ret == 0)
						write(fifo_fd[1], data, param->size);
				}
				break;
			case PACKID_REG:
				{
					struct pack_reg_ret ret;

					ret.ret = -E_NOT_IMPLEMENTED;
					if (qemu_callback.get_reg)
						ret.ret = qemu_callback.get_reg(ret.regs, &ret.nreg);
					write_pack(fifo_fd[1], PACKID_MEM_DUMP_RET, &ret, sizeof(ret));
				}
				break;
			case PACKID_CONTINUE:
				cont = 1;
				break;
			default:
				fprintf(stderr, "qemu: invalid package id from chipmuck\n");
				break;
			};
		}
	}
}

void simconn_set_callback(struct simconn_callback *callback) {
	qemu_callback = *callback;
}

